package com.guaoran.distributed.io.nio.one.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

/**
 * @author : guaoran
 * @Description NIOClient
 * @DESC: 模拟聊天
 * @date :2019/4/18 22:04
 */
public class NIOClient {
    //相当于自定义协议格式，与客户端协商好
    private final static String USER_CONTENT_SPLIT = "#@#";
    private final static String USER_EXIST = "系统提示：该昵称已存在，请换一个试试";
    private Charset charset = Charset.forName("UTF-8");

    private String nickName;
    private Selector selector;
    private SocketChannel client;

    public NIOClient() throws IOException {
        // 连接远程主机的ip和端口
        client = SocketChannel.open(new InetSocketAddress("localhost",8080));
        // 设置非阻塞
        client.configureBlocking(false);
        // 打开选择器
        selector = Selector.open();
        client.register(selector, SelectionKey.OP_READ);
    }
    // 发送消息
    public void sendMessage(){
        //开辟一个新线程从服务器端读数据
        new Reader().start();
        //开辟一个新线程往服务器端写数据
        new Writer().start();
    }
    private class Writer extends Thread{
        @Override
        public void run() {
            try {
                //在主线程中 从键盘读取数据输入到服务器端
                Scanner scanner = new Scanner(System.in);
                while (scanner.hasNextLine()){
                    String line = scanner.nextLine();
                    if("".equals(line)) continue; //不允许发空消息
                    if(nickName == null || "".equals(nickName)) {
                        nickName = line;
                        line = nickName + USER_CONTENT_SPLIT;
                    } else {
                        line = nickName + USER_CONTENT_SPLIT + line;
                    }
                    client.write(charset.encode(line));//client既能写也能读，这边是写
                }
                scanner.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    private class Reader extends Thread{
        @Override
        public void run() {
            try {
                for(;;){
                    int readyChannels = selector.select();
                    if(readyChannels == 0) {
                        continue;
                    }
                    Set<SelectionKey> selectedKeys = selector.selectedKeys();  //可以通过这个方法，知道可用通道的集合
                    Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
                    while(keyIterator.hasNext()) {
                        SelectionKey key = (SelectionKey) keyIterator.next();
                        keyIterator.remove();
                        process(key);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        private void process(SelectionKey key) throws IOException {
            if(key.isReadable()){
                //使用 NIO 读取 Channel中的数据，这个和全局变量client是一样的，因为只注册了一个SocketChannel
                //client既能写也能读，这边是读
                SocketChannel sc = (SocketChannel)key.channel();

                ByteBuffer buff = ByteBuffer.allocate(1024);
                String content = "";
                while(sc.read(buff) > 0)
                {
                    buff.flip();
                    content += charset.decode(buff);
                }
                //若系统发送通知名字已经存在，则需要换个昵称
                if(USER_EXIST.equals(content)) {
                    nickName = "";
                }
                System.out.println(content);
                key.interestOps(SelectionKey.OP_READ);
            }
        }
    }
    public static void main(String[] args) throws IOException {
        new NIOClient().sendMessage();
    }
}
